<?php
/**
 * Response
 * @filesource
 */
/**
 * Namespace
 */
namespace framework\library;
/**
 * Dependencies
 */
use framework\library\interfaces\Response_Interface;
use framework\Front;
/**
 * Response Class
 *
 * Plurious Core
 *
 * Provides convenience methods for normal HTTP responses
 * 
 * @author Randy Casburn <randy@plurious.com>
 * @copyright Copyright Plurious Software, all rights reserved
 * @license MIT
 * @license http://www.opensource.org/licenses/mit-license.html
 * @link http://plurious.com/framework/documentation/classes/Front.html
 * @version 2.1
 * 
 * @uses framework/Front
 */
class Response implements Response_Interface {

    private $headers = array();
    private $level = 0;

    public function __construct() {
        ob_start();
    }

    /**
     * Adds arbitrary headers to the response
     * 
     * Must be full string headers ready for output
     * 
     * @see $this->redirect() for example usage
     * @param string $header 
     */
    public function addHeader($header) {
        $this->headers[] = $header;
    }

    /**
     * Sets a cookie to be sent with other headers
     */
    public function setCookie($cookie) {
        $this->addHeader("set-cookie: $cookie");
    }

    /**
     * Prevents caching by the browser
     */
    public function noCache() {
        $this->addHeader("cache-Control: no-store, no-cache, must-revalidate");
        $this->addHeader("cache-Control: post-check=0, pre-check=0");
        // HTTP/1.0
        $this->addHeader("Pragma: no-cache");
        // Date in the past
        $this->addHeader("Expires: -1");
        // always modified
        $this->addHeader("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    }

    /**
     * Sends a redirect header that immediately redirects the client
     * 
     * Exits script execution
     * 
     * NOTE: This has been unit tested, but the exit statement prevents PHPUnit
     * from continuing.  There are two options for testing this method:
     * 1. comment out the exit() statement and test code in ResponseTest.php
     * 2. Add Sebastian's PHPUnit test-helper to your php configuration
     * 
     * @param string $url
     * @param int $status 
     */
    public function redirect($url, $status = 302) {
        header('Status: ' . $status);
        header('Location: ' . str_replace('&amp;', '&', $url));
        ob_end_clean();
        if(!PHPUNIT){
            // @codeCoverageIgnoreStart
            exit();
            // @codeCoverageIgnoreEnd
        }
    }

    /**
     * Sets the gzip compression level (0[none] to 9[max])
     * 
     * @param int $level 
     */
    public function setCompression($level) {
        $this->level = $level;
    }

    /**
     * Determines browser capability for compression and applies compression
     * and returns compressed data if possible. Otherwise returns uncompressed data.
     * 
     * @uses Registry
     * @uses Request
     * @param string $data Data to be compressed
     * @param int $level The g-zip compression level 0[none] to 9[max]
     * @return string The compressed or uncompressed output string
     */
    public function compress($data, $level = 0) {
        // Cannot compress if any headers already sent
        if (headers_sent()) {
            // @codeCoverageIgnoreStart
            // Will not be concerned if data is simply returned
            return $data;
            // @codeCoverageIgnoreEnd
        }
        // Cannot compress if connection is not == CONNECTION_NORMAL 
        if (connection_status() !== CONNECTION_NORMAL) {
            // @codeCoverageIgnoreStart
            // Will not be concerned if data is simply returned
            return $data;
            // @codeCoverageIgnoreEnd
        }
        $this->registry = Front::getInstance();
        $request = $this->registry->get('request');
        $client_can = $request->getServer('HTTP_ACCEPT_ENCODING');
        $encoding = strpos($client_can, 'gzip');
        if ($encoding === false) {
            // @codeCoverageIgnoreStart
            // Will not be concerned if data is simply returned
            return $data;
            // @codeCoverageIgnoreEnd
        }
        // Made it...let's continue with compression if there is enough data
        if (strlen($data) > 2048) {
            $data = gzcompress($data, (int) $level);
            $this->addHeader('Content-Encoding: gzip');
            $this->addHeader('Content-Length: ' . strlen($data));
        }
        return $data;
    }

    /**
     * Sends the very final response (possible in compressed form)
     * 
     * @todo finish json and feed implementations
     * 
     * @param string $output The Page content
     * @param string $type One of document, json, atom, rss2.0
     */
    public function output($output, $type = 'document') {
        if (empty($output)) {
            throw new \InvalidArgumentException("The output data must be provided.");
        }
        if ($type == 'json' && is_array($output)) {
            $output = json_encode($output);
        }
        if ($this->level) {
            $output = $this->compress($output, $this->level);
        }
        if (!headers_sent()) {
            foreach ($this->headers as $header) {
                header($header, true);
            }
        }
        echo $output;
        ob_end_flush();
    }

    public function getHeaders() {
        return $this->headers;
    }

    public function getCompression() {
        return $this->level;
    }

}

?>
